#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using UTable.Input;
using UTable.Input.MultiTouch;
using UTable.ObjectManager.Commands;
using UTable.Objects.Handlers;
using UTable.Objects.Policies;
using UTable.Objects.Handlers.Gestures;

namespace UTable.Objects.Controls
{
    public class UObject : UContentControl, IVisualTree, IObject
    {
        #region Private Fields

        private ControlInfoTree _controlTree;

        private UObjectEntity _entity;

		private Canvas _popupStage;
		private UBorder _popupBackground;
        private Point _defaultPosition = new Point(0, 0);
        private double _defaultOrientation = 0;
        private Geometry _defaultClip = null;
        private Size _defaultScale = new Size(1, 1);
        private Size _defaultSize = new Size(100, 100);
        private Size _defaultMinSize = new Size(0, 0);
        private Size _defaultMaxSize = new Size(double.MaxValue, double.MaxValue);
        private Point _defaultRotateCenter = new Point(0, 0);
        private Point _defaultScaleCenter = new Point(0, 0);
        private Visibility _defaultVisibility = Visibility.Visible;
        private double _defaultOpacity = 1;
        private bool _defaultIsHitTestEnabled = true;
        private bool _defaultIsHitTestVisibile = true;
        private ObjectLayoutState _defaultLayoutState = ObjectLayoutState.Normal;
        private ObjectOperatingState _defaultOperatingState = ObjectOperatingState.Automatic;
        private int _defaultZIndex = 0;
        private bool _defaultCanHoldObjects = false;
        private bool _defaultNotifyLayoutChangeEnabled = true;
        private Type _layoutPolicyType = null;
        private object _layoutPolicyParameter = null;
        private UElementMenu _menu = new UElementMenu();
        private UElementMenuItem _closeMenuItem;
        private UElementMenuItem _operatingStateMenuItem;
        private UElementMenuItem _layoutStateMenuItem;
        private UElementMenuItem _pinMenuItem;
        private Grid _stage;
        private UCanvas _hitMaskCanvas = new UCanvas();
		private bool _focusOnPopup = false;

        private bool _savedShowActionEffects;
        private Point _savedMenuPosition;
        private double _savedMenuOrientation;
        private double _savedMenuAngle;
        private double _savedMenuRadius;

        LocalGestureEngine engine;
        
        #endregion

        #region Private Methods

        private void OnMinWidthChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.MinWidth, this.MinWidth);
        }
        private void OnMaxWidthChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.MaxWidth, this.MaxWidth);
        }
        private void OnMinHeightChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.MinHeight, this.MinHeight);
        }
        private void OnMaxHeightChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.MaxHeight, this.MaxHeight);
        }
        private void OnWidthChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.Width, this.Width);
        }
        private void OnHeightChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.Height, this.Height);
        }
        private void OnOpacityChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.Opacity, this.Opacity);
        }
        private void OnVisibilityChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.Visibility, this.Visibility);
        }
        private void OnIsHitTestVisibleChanged(object sender, EventArgs args)
        {
            SetProperty(UObjectEntity.PropertyType.IsHitTestVisible, this.IsHitTestVisible);
        }
        private void OnBackgroundChanged(object sender, EventArgs args)
        {
            // Brush background = this.Background.CloneCurrentValue();
            // background.Freeze();
            // SetProperty(UObjectEntity.PropertyType.Background, background);
        }
        private void OnClipChanged(object sender, EventArgs args)
        {
            Geometry g = null;
            if (this.Clip != null)
            {
                g = Clip.CloneCurrentValue();
                g.Freeze();
            }
            SetProperty(UObjectEntity.PropertyType.Clip, g);
        }

        private static void OnShowMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            /*
            if (d is UObject)
            {
                UObject o = d as UObject;
                if (o.Menu.IsLoaded)
                {
                    if (o.ShowMenu)
                    {
                        o.Menu.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        o.Menu.Visibility = Visibility.Collapsed;
                    }
                }
            }
            */
        }


        private static void OnMenuDisplayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is UObject)
                (d as UObject).OnMenuDisplayChanged();
        }

        //Fix: Has bug on HoldToShow
        private void OnMenuDisplayChanged()
        {
            if (Menu.IsLoaded)
            {
                if (MenuDisplay == UObjectMenuDisplayType.AlwaysShow)
                {
                    Menu.Visibility = Visibility.Visible;
                }
                else
                {
                    Menu.Visibility = Visibility.Collapsed;
                }
            }
            if (MenuDisplay == UObjectMenuDisplayType.HoldToShow)
            {
                if (engine == null)
                {
                    engine = new LocalGestureEngine(this, this);
                    engine.StopRouteDown = false;
                    engine.HandleAllEvents = false;
                    OneContactGestureRecognizer recognizer1 = new OneContactGestureRecognizer();
                    recognizer1.EnabledGestures.Add(OneContactGestureType.Hold);
                    recognizer1.EnabledGestures.Add(OneContactGestureType.Tap);
                    engine.RegisterGesture(recognizer1);
                    engine.GestureDetected += new UTable.Objects.Handlers.Gestures.GestureDetectedEventHandler(OnGestureDetected);
                    Menu.SubmenuClosed += new EventHandler(Menu_SubmenuClosed);    
                }
                MultiTouchProcessor.Handlers.Add(engine);
            }
            else
            {
                MultiTouchProcessor.Handlers.Remove(engine);
            }
        }

        void Menu_SubmenuClosed(object sender, EventArgs e)
        {
            if (MenuDisplay == UObjectMenuDisplayType.HoldToShow)
            {
                Menu.Visibility = Visibility.Collapsed;
            }
        }

        void OnGestureDetected(GestureRecognizer sender, UTable.Objects.Handlers.Gestures.GestureDetectedEventArgs args)
        {
            Gesture gesture = args.DetectedGesture;

            if (gesture is HoldGesture)
            {
                Canvas.SetTop(Menu, (gesture as HoldGesture).Position.Y - 20);
                Canvas.SetLeft(Menu, (gesture as HoldGesture).Position.X - 20);
                Menu.Visibility = Visibility.Visible;
                Menu.FullOpenSubmenu(true);
            }
            else if (gesture is TapGesture)
            {
                Menu.CloseSubmenu(true);
            }
        }

        void _holdHandler_FingerHolded(FingersHoldHandler sender, FingersHoldEventArgs args)
        {
            Canvas.SetTop(Menu, args.Position.Y - 20);
            Canvas.SetLeft(Menu, args.Position.X - 20);
            Menu.Visibility = Visibility.Visible;
            Menu.FullOpenSubmenu(true);

            // raise the finger down event on that menu so that it can be open when it is shown
            // FingerEventArgs a = new FingerEventArgs(FingerEventType.FINGER_DOWN_EVENT, args.FingerID, new Point(20, 20));
            // Collection<FingerEventArgs> c = new Collection<FingerEventArgs>();
            // c.Add(a);
            // Menu.RaiseEvent(new MultiTouchEventArgs(c, 0));
        }

        private static void OnMenuPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is UObject)
            {
                UObject o = d as UObject;
                Canvas.SetTop(o.Menu, o.MenuPosition.Y);
                Canvas.SetLeft(o.Menu, o.MenuPosition.X);
            }
        }

        private static void OnMenuOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is UObject)
            {
                UObject o = d as UObject;
                o.Menu.Orientation = o.MenuOrientation;
            }
        }
        
        private void TurnToEdit()
        {
            _hitMaskCanvas.Background = null;
        }

        private void TurnToView()
        {
            _hitMaskCanvas.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
        }


		private static void OnActiveChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
		{
			(obj as UObject).OnActiveChanged();
		}

		private void OnActiveChanged()
		{
			if (IsActive && Activated != null)
			{
				Activated(this, EventArgs.Empty);
			}
			if (!IsActive && Unactivated != null)
			{
				Unactivated(this, EventArgs.Empty);
			}
		}

        #endregion

        #region Constructors

        static UObject()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(UObject), new FrameworkPropertyMetadata(typeof(UObject)));
        }

        public UObject()
            : base()
        {
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                TurnToEdit();
                _controlTree = new ControlInfoTree(this.Info);
                DependencyPropertyDescriptor.FromProperty(UObject.MinWidthProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnMinWidthChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.MaxWidthProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnMaxWidthChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.MaxHeightProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnMaxHeightChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.MinHeightProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnMinHeightChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.WidthProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnWidthChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.HeightProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnHeightChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.IsHitTestVisibleProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnIsHitTestVisibleChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.OpacityProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnOpacityChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.VisibilityProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnVisibilityChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.ClipProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnClipChanged));
                DependencyPropertyDescriptor.FromProperty(UObject.ClipProperty,
                    typeof(UObject)).AddValueChanged(this, new EventHandler(OnBackgroundChanged));


                this.Loaded += new RoutedEventHandler(UObject_Loaded);
                this.Unloaded += new RoutedEventHandler(UObject_Unloaded);

                // set the local input manager
                InputManager = new InputManagerImpl();
                LocalInputProvider = new LocalInputProvider();
                InputManager.VisualTree = this;
                InputManager.RegisterInputProvider(typeof(InputEventArgs), LocalInputProvider);
                // LocalMultitouchInputMapper multitouchMapper = new LocalMultitouchInputMapper();
                // InputManager.RegisterInputMapper(typeof(MultiTouchEventArgs), multitouchMapper);
                InputManager.InputDetected += new ManagedInputEventHandler(InputManager_InputDetected);

                this.LayoutPolicyType = typeof(ObjectLayoutPolicy);
            }
        }

        void UObject_Unloaded(object sender, RoutedEventArgs e)
        {
            // Console.WriteLine(this + " Unloaded");
        }

        List<FingerEventArgs> unneededFingerEvents = new List<FingerEventArgs>();

        public void InputManager_InputDetected(ManagedInputEventArgs args)
        {
            if (args.Args is MultiTouchEventArgs)
                (args.Args as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.UObjectTime = InputEventArgs.GetTimestamp();
            // (args.Args as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.SetStep(4);


            // Raise Routed Event
            IVisualNode node = args.Receiver;
            bool sourceSet = true;
            if (args.Args.OriginalSource == null)
            {
                sourceSet = false;
                args.Args.OriginalSource = args.Receiver;
            }

            if (BeforeInputReceived(args.Args as InputEventArgs))
            {
                while (node != null && (!sourceSet || node != args.Args.OriginalSource))
                {
                    node.RaiseEvent(args.Args as InputEventArgs);
                    if (!args.Args.RouteDown)
                    {
                        break;
                    }
                    // hard code - -
                    IVisualNode parent = this.GetParent(node);
                    if (parent != null)
                    {
                        if (args.Args is MultiTouchEventArgs)
                        {
                            Collection<FingerEventArgs> fingerEvents = (args.Args as MultiTouchEventArgs).FingerEvents;
                            foreach (FingerEventArgs f in fingerEvents)
                            {
                                f.Position = this.TranslatePoint(node, f.Position, parent);
                                // the finger in event and finger out event should not be routed down
                                if (f.EventType == FingerEventType.FINGER_IN_EVENT || f.EventType == FingerEventType.FINGER_OUT_EVENT)
                                {
                                    unneededFingerEvents.Add(f);
                                }
                            }
                            foreach (FingerEventArgs f in unneededFingerEvents)
                            {
                                fingerEvents.Remove(f);
                            }
                            unneededFingerEvents.Clear();
                        }
                    }
                    node = parent;
                }
            }

            // (args.Receiver as ControlInfo).Control.RaiseEvent(args.Args as InputEventArgs);
            if (node == null && !args.Args.Handled && !Pinned)
            {
                // inform the object to handle the input event
                args.Args.OriginalSource = args.Receiver;
                UTableHelper.PutCommand(new ObjectCommand(this.Entity, ObjectCommandType.NotMappedInput, args.Args));
            }
        }

        /// <summary>
        /// Raise before the inputs route along the object
        /// </summary>
        /// <param name="args">The input received</param>
        /// <returns>Whether to allow inputs route in this object </returns>
        protected virtual bool BeforeInputReceived(InputEventArgs args)
        {
            return true;
        }

        void UObject_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.ObjectLoaded != null)
                ObjectLoaded(this, null);
            // this.Info.Fresh();

            // We should fresh the menu visibility here
            // since the menu must be loaded before the visibility can be set
            // otherwise, exception will be thrown
            if (MenuDisplay == UObjectMenuDisplayType.AlwaysShow)
            {
                Menu.Visibility = Visibility.Visible;
            }
            else
            {
                Menu.Visibility = Visibility.Collapsed;
            }

			// check whether this object owns an entity
			this.OwnEntity = UTableHelper.CurrentVisualTree == this;
			// if (!this.OwnEntity)
				

            // refresh the orientation and position
            // I don't know why the default value cannot invoke such an event
            OnMenuOrientationChanged(this, new DependencyPropertyChangedEventArgs());
            OnMenuPositionChanged(this, new DependencyPropertyChangedEventArgs());
        }

        #endregion

        #region Properties

        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This attribute is not supported in this class.", true)]
        public new Transform RenderTransform { get; set; }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This attribute is not supported in this class.", true)]
        public new Transform LayoutTransform { get; set; }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This attribute is not supported in this class.", true)]
        public new Point RenderTransformOrigin { get; set; }


        /// <summary>
        /// Gets or sets the type of the layout policy
        /// </summary>
        public Type LayoutPolicyType
        {
            get { return _layoutPolicyType; }
            set
            {
                _layoutPolicyType = value;
                if (!IsDesigning)
                {
                    UTableHelper.SetLayoutPolicy(this, value, LayoutPolicyParameter);
                }
            }
        }

        /// <summary>
        /// Gets or sets the parameter for the current layout policy
        /// This parameter should depend on the layout policy type
        /// </summary>
        public object LayoutPolicyParameter
        {
            get { return _layoutPolicyParameter; }
            set
            {
                _layoutPolicyParameter = value;
                if (!IsDesigning)
                {
                    UTableHelper.SetLayoutPolicy(this, LayoutPolicyType, value);
                }
            }
        }

        // not open to the users currently
        internal Type AuthorizationPolicyType { get; set; }

        // not open to the users currently
        internal Type ConflictResolvePolicyType { get; set; }

        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            internal set { SetValue(IsActiveProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsActive.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsActiveProperty =
			DependencyProperty.Register("IsActive", typeof(bool), typeof(UObject), new UIPropertyMetadata(false, new PropertyChangedCallback(OnActiveChanged)));

        public bool ShowsActivationEffects
        {
            get { return (bool)GetValue(ShowsActivationEffectsProperty); }
            set { SetValue(ShowsActivationEffectsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShowsActivationEffects.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowsActivationEffectsProperty =
            DependencyProperty.Register("ShowsActivationEffects", typeof(bool), typeof(UObject), new UIPropertyMetadata(true));


		public UObjectActivationEffectType ActivationEffectType
		{
			get { return (UObjectActivationEffectType)GetValue(ActivationEffectTypeProperty); }
			set { SetValue(ActivationEffectTypeProperty, value); }
		}

		public static readonly DependencyProperty ActivationEffectTypeProperty =
			DependencyProperty.Register("ActivationEffectType", typeof(UObjectActivationEffectType), typeof(UObject), new UIPropertyMetadata(UObjectActivationEffectType.SheenAndBorder));

        public Point MenuPosition
        {
            get { return (Point)GetValue(MenuPositionProperty); }
            set 
            { 
                SetValue(MenuPositionProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for MenuPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MenuPositionProperty =
            DependencyProperty.Register("MenuPosition", typeof(Point), typeof(UObject), new FrameworkPropertyMetadata(new Point(0, 0), new PropertyChangedCallback(OnMenuPositionChanged)));

        public UElementMenu Menu
        {
            get { return _menu; }
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This attribute is not supported in this class.", true)]
        private bool ShowMenu
        {
            get { return (bool)GetValue(ShowMenuProperty); }
            set 
            {
                SetValue(ShowMenuProperty, value); 
            }
        }

        public static readonly DependencyProperty ShowMenuProperty =
            DependencyProperty.Register("ShowMenu", typeof(bool), typeof(UObject), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnShowMenuChanged)));


        public UObjectMenuDisplayType MenuDisplay
        {
            get { return (UObjectMenuDisplayType)GetValue(MenuDisplayProperty); }
            set { SetValue(MenuDisplayProperty, value); }
        }

        public static readonly DependencyProperty MenuDisplayProperty =
            DependencyProperty.Register("MenuDisplay", typeof(UObjectMenuDisplayType), typeof(UObject), new UIPropertyMetadata(UObjectMenuDisplayType.AlwaysShow, new PropertyChangedCallback(OnMenuDisplayChanged)));


        public double MenuOrientation
        {
            get { return (double)GetValue(MenuOrientationProperty); }
            set { SetValue(MenuOrientationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MenuOrientation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MenuOrientationProperty =
            DependencyProperty.Register("MenuOrientation", typeof(double), typeof(UObject), new FrameworkPropertyMetadata(-45.0, new PropertyChangedCallback(OnMenuOrientationChanged)));



        /// <summary>
        /// Gets or sets an object's icon. This is a dependency property.
        /// An object's title will be displayed at the AppLoader
        /// </summary>
        public ImageSource Icon
        {
            get { return (ImageSource)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }

        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register("Icon", typeof(ImageSource), typeof(UObject), new UIPropertyMetadata(null));


        /// <summary>
        /// Gets or sets an object's title. This is a dependency property.
        /// An object's title will be displayed at the AppLoader
        /// </summary>
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(UObject), new UIPropertyMetadata("UObject"));


        /// <summary>
        /// Gets or sets whether this object is an main object
        /// Only objects that is main
        /// </summary>
        public bool IsMain
        {
            get { return (bool)GetValue(IsMainProperty); }
            set { SetValue(IsMainProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMain.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMainProperty =
            DependencyProperty.Register("IsMain", typeof(bool), typeof(UObject), new UIPropertyMetadata(false));


		/// <summary>
		/// Gets or sets the corner radius of the object
		/// </summary>
		public CornerRadius CornerRadius
		{
			get { return (CornerRadius)GetValue(CornerRadiusProperty); }
			set { SetValue(CornerRadiusProperty, value); }
		}

		// Using a DependencyProperty as the backing store for CornerRadius.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty CornerRadiusProperty =
			DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(UObject), new UIPropertyMetadata(new CornerRadius()));

		/// <summary>
		/// Gets or sets the value that indicates whether this object owns entity. 
		/// </summary>
		public bool OwnEntity
		{
			get { return (bool)GetValue(OwnEntityProperty); }
			internal set { SetValue(OwnEntityProperty, value); }
		}

		public static readonly DependencyProperty OwnEntityProperty =
			DependencyProperty.Register("OwnEntity", typeof(bool), typeof(UObject), new UIPropertyMetadata(true));


        public bool Pinned
        {
            get { return (bool)GetValue(PinnedProperty); }
            set { SetValue(PinnedProperty, value); }
        }

        public static readonly DependencyProperty PinnedProperty =
            DependencyProperty.Register("Pinned", typeof(bool), typeof(UObject), new UIPropertyMetadata(false));


        #endregion

		#region Events

		public event EventHandler Activated;

		public event EventHandler Unactivated;

		#endregion

        #region Internal Events

        internal delegate void LayoutPropertyChangedEventHandler(LayoutPropertyChangedEventArgs args);

        internal class LayoutPropertyChangedEventArgs : EventArgs
        {
            public LayoutPropertyChangedEventArgs(LayoutPropertyType type, object newValue)
            {
                this.PropertyType = type;
                this.NewValue = newValue;
            }

            public LayoutPropertyType PropertyType;

            public object NewValue;
        }

        internal enum LayoutPropertyType
        {
            Width,
            Height,
            MinWidth,
            MinHeight,
            MaxWidth,
            MaxHeight,
            Clip,
            Visibility,
            IsHitTestVisible,
            IsHitTestEnabled,
            Opacity,
            Background
        }

        #endregion

        #region Internal Methods

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            OnMenuIntializing();

            _stage = this.GetTemplateChild("PART_Stage") as Grid;
			_popupStage = this.GetTemplateChild("PART_PopupStage") as Canvas;
			_popupBackground = this.GetTemplateChild("PART_PopupBackgound") as UBorder;
            _hitMaskCanvas.Children.Add(Menu);
            _stage.Children.Add(_hitMaskCanvas);
            Menu.SubmenuOpened += new EventHandler(Menu_SubmenuOpened);
            Menu.CommandReceived += new ElementMenuCommandReceivedEventHandler(Menu_CommandReceived);
        }

        protected virtual void OnMenuIntializing()
        {
            // Set the default menu items
            _closeMenuItem = new UElementMenuItem();
            _closeMenuItem.Header = "Close";
            _closeMenuItem.Command = "Close";
            Menu.Items.Add(_closeMenuItem);
            _operatingStateMenuItem = new UElementMenuItem();
            _operatingStateMenuItem.Header = "View";
            _operatingStateMenuItem.Command = "SwitchOperatingState";
            Menu.Items.Add(_operatingStateMenuItem);
            _layoutStateMenuItem = new UElementMenuItem();
            _layoutStateMenuItem.Header = "Maximized";
            _layoutStateMenuItem.Command = "SwitchLayoutState";
            Menu.Items.Add(_layoutStateMenuItem);
            _pinMenuItem = new UElementMenuItem();
            _pinMenuItem.Header = "Pin";
            _pinMenuItem.Command = "Pin";
            Menu.Items.Add(_pinMenuItem);
        }

        protected virtual void OnMenuCommandReceived(UElementMenu sender, ElementMenuCommandReceivedEventArgs args)
        {
            switch (args.Command)
            {
                case "Close":
                    this.Close();
                    break;
                case "SwitchLayoutState":
                    if (this.LayoutState == ObjectLayoutState.Maximized || this.LayoutState == ObjectLayoutState.Minimized)
                        this.LayoutState = ObjectLayoutState.Normal;
                    else
                        this.LayoutState = ObjectLayoutState.Maximized;
                    break;
                case "SwitchOperatingState":
                    if (this.OperatingState == ObjectOperatingState.Editing || this.OperatingState == ObjectOperatingState.Automatic)
                        this.OperatingState = ObjectOperatingState.Viewing;
                    else
                        this.OperatingState = ObjectOperatingState.Editing;
                    break;
                case "Pin":
                    this.Pinned = !this.Pinned;
                    break;
            }
        }

        protected virtual void OnMenuOpened()
        {
            if (this.OperatingState == ObjectOperatingState.Editing || this.OperatingState == ObjectOperatingState.Automatic)
            {
                _operatingStateMenuItem.Header = "View";
            }
            if (this.OperatingState == ObjectOperatingState.Viewing)
            {
                _operatingStateMenuItem.Header = "Edit";
            }
            if (this.LayoutState == ObjectLayoutState.Maximized || this.LayoutState == ObjectLayoutState.Minimized)
            {
                _layoutStateMenuItem.Header = "Restore";
            }
            if (this.LayoutState == ObjectLayoutState.Normal)
            {
                _layoutStateMenuItem.Header = "Maximize";
            }
            if (this.Pinned)
            {
                _pinMenuItem.Header = "Unpin";
            }
            else
            {
                _pinMenuItem.Header = "Pin";
            }
        }

        void Menu_CommandReceived(UElementMenu sender, ElementMenuCommandReceivedEventArgs args)
        {
            OnMenuCommandReceived(sender, args);
        }

        void Menu_SubmenuOpened(object sender, EventArgs e)
        {
            OnMenuOpened();
        }

        internal void ChangeLayoutProperty(LayoutPropertyChangedEventArgs args)
        {
            switch (args.PropertyType)
            {
                case LayoutPropertyType.Width:
                    this.Width = (double)args.NewValue;
                    break;
                case LayoutPropertyType.Height:
                    this.Height = (double)args.NewValue;
                    break;
                case LayoutPropertyType.MinWidth:
                    this.MinWidth = (double)args.NewValue;
                    break;
                case LayoutPropertyType.MinHeight:
                    this.MinHeight = (double)args.NewValue;
                    break;
                case LayoutPropertyType.MaxWidth:
                    this.MaxWidth = (double)args.NewValue;
                    break;
                case LayoutPropertyType.MaxHeight:
                    this.MaxHeight = (double)args.NewValue;
                    break;
                case LayoutPropertyType.Clip:
                    this.Clip = (Geometry)args.NewValue;
                    break;
                case LayoutPropertyType.Opacity:
                    this.Opacity = (double)args.NewValue;
                    break;
                case LayoutPropertyType.Visibility:
                    this.Visibility = (Visibility)args.NewValue;
                    break;
                case LayoutPropertyType.IsHitTestEnabled:
                    this.IsHitTestEnabled = (bool)args.NewValue;
                    break;
                case LayoutPropertyType.IsHitTestVisible:
                    this.IsHitTestVisible = (bool)args.NewValue;
                    break;
            }
        }

        /// <summary>
        /// let this object receive command sent from the object manager,
        /// and dispatch the commands to the corresponding control (invoke events)
        /// </summary>
        /// <param name="cmds"></param>
        internal void ExecuteContentCommand(ContentCommand cmd)
        {
            switch (cmd.SubType)
            {
                case ContentCommandType.Input:
                    //(cmd.Param as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.AfterSendUObjectTime = InputEventArgs.GetTimestamp();
                    // (cmd.Param as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.SetStep(3);
                    LocalInputProvider.RaiseInputEvent(cmd.Param as InputEventArgs);
                    break;
            }
        }

        #endregion

		#region Share

		/// <summary>
		/// Called when the model of this object is initializing
		/// </summary>
		/// <param name="model">the reference model that is used to update this model</param>
		public virtual void InitializeModel(object model)
		{

		}

		/// <summary>
		/// Get the model of this object
		/// </summary>
		public virtual object GetModel()
		{
			return null;
		}

		/// <summary>
		/// Called when a update-model request comes
		/// This method should be implemented by the client 
		/// </summary>
		/// <param name="updateMessage"></param>
		public virtual void UpdateModel(object updateMessage)
		{

		}

		/// <summary>
		/// Notify other objects that share this object to update their models
		/// </summary>
		/// <param name="updateMessage"></param>
		public void NotifyModelUpdated(object updateMessage)
		{
			UTableHelper.ShareCenter.UpdateModel(this, new UpdateModelMessage(updateMessage));
		}

		private bool _isShareMaster;

		public bool IsShareMaster
		{
			get { return _isShareMaster; }
			internal set { _isShareMaster = value; }
		}

		#endregion

		#region Internal Properties

		/// <summary>
        /// The local input manager
        /// get input from global input manager
        /// </summary>
        internal IInputManager InputManager { get; set; }

        internal LocalInputProvider LocalInputProvider { get; set; }

		internal PresentationSource PresentationSource { get; set; }

        internal ControlInfoTree ControlTree
        {
            get { return _controlTree; }
        }

        internal UObjectEntity Entity
        {
            get { return _entity; }
            set { _entity = value; }
        }

        internal bool IsDesigning = true;

		public Canvas PopupStage { get { return _popupStage; } }

		internal bool FocusOnPopup
		{
			get
			{
				return _focusOnPopup;
			}
			set
			{
				_focusOnPopup = value;
				if (value)
				{
					this._popupBackground.Visibility = Visibility.Visible;
				}
				else
				{
					this._popupBackground.Visibility = Visibility.Collapsed;
				}
			}
		}

		internal bool IsHitTestEnabled { 
			get { return true; }
			set {}
		}

        #endregion

		#region IVisualTree Members

		public IVisualNode TreeRoot
		{
			get { return this; }
		}

		public double TreeWidth
		{
			get { return this.Width; }
		}

		public double TreeHeight
		{
			get { return this.Height; }
		}

		private IVisualNode GetNodeByID(IVisualNode node, string id)
		{
			if (node.ID == id)
				return node;
			else
			{
				List<IVisualNode> children = GetChildren(node as DependencyObject);
				foreach (IVisualNode child in children)
				{
					IVisualNode childNode = GetNodeByID(child, id);
					if (childNode != null)
						return childNode;
				}
			}
			return null;
		}

		public IVisualNode GetNodeByID(string id)
		{
			return GetNodeByID(this, id);
		}

		public IVisualNode GetParent(IVisualNode node)
		{
			return GetParent(node as DependencyObject);
		}

		private List<IVisualNode> GetChildren(DependencyObject obj)
		{
			List<IVisualNode> lst = new List<IVisualNode>();
			int count = VisualTreeHelper.GetChildrenCount(obj);
			for (int i = 0; i < count; i++)
			{
				DependencyObject child = VisualTreeHelper.GetChild(obj, i);
				if (child is IVisualNode)
				{
					lst.Add(child as IVisualNode);
				}
				else
				{
					lst.AddRange(GetChildren(child));
				}
			}
			return lst;
		}

		private IVisualNode GetParent(DependencyObject obj)
		{
			DependencyObject parent = VisualTreeHelper.GetParent(obj);
			while (parent != null && !(parent is IVisualNode))
			{
				parent = VisualTreeHelper.GetParent(parent);
			}
			return parent as IVisualNode;
		}

		public IVisualNode GetChild(IVisualNode node, int index)
		{
			return GetChildren(node as DependencyObject)[index];
		}

		public int GetChildrenCount(IVisualNode node)
		{
			return GetChildren(node as DependencyObject).Count;
		}

		public HitTestResult HitTest(HitTestParameters hitTestParameters)
		{
			if (hitTestParameters is PointHitTestParameters)
			{
				System.Windows.Media.HitTestResult result = null;

				VisualTreeHelper.HitTest(this, new HitTestFilterCallback(HitTestFilterCallback),
					delegate(System.Windows.Media.HitTestResult r)
					{
						result = r;
						return HitTestResultBehavior.Stop;
					},
					new System.Windows.Media.PointHitTestParameters((hitTestParameters as PointHitTestParameters).HitPoint));
				if (result != null && result.VisualHit != null)
				{
					if (result.VisualHit is IVisualNode)
						return new HitTestResult(result.VisualHit as IVisualNode);
					else
						return new HitTestResult(GetParent(result.VisualHit));
				}

			}
			return new HitTestResult(null);
		}

		HitTestFilterBehavior HitTestFilterCallback(DependencyObject potentialHitTestTarget)
		{
			if (potentialHitTestTarget is UIElement)
			{
				UIElement target = potentialHitTestTarget as UIElement;
				if (target.Opacity == 0)
					return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
				if (target.Visibility == Visibility.Hidden || target.Visibility == Visibility.Collapsed)
					return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
				if (target.IsHitTestVisible == false)
					return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
			}
			return HitTestFilterBehavior.Continue;
		}

		public Point TranslatePoint(IVisualNode node, Point point, IVisualNode relativeTo)
		{
			return (node as UIElement).TranslatePoint(point, relativeTo as UIElement);
		}

		public Point TranslatePoint(Point point, IVisualNode relativeTo)
		{
			return this.TranslatePoint(point, relativeTo as UIElement);
		}

		#endregion

        #region IObject Members

        private object GetProperty(UObjectEntity.PropertyType type)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                switch (type)
                {
                    case UObjectEntity.PropertyType.CanHoldObjects:
                        return true;
                    case UObjectEntity.PropertyType.NotifyLayoutChangeEnabled:
                        return true;
                    case UObjectEntity.PropertyType.Height:
                        return this.Height;
                    case UObjectEntity.PropertyType.Width:
                        return this.Width;
                    case UObjectEntity.PropertyType.OwnedObjects:
                        return new ReadOnlyCollection<IObject>(new List<IObject>());
                    case UObjectEntity.PropertyType.OperatingState:
                        return ObjectOperatingState.Automatic;
                }
                return null;
            }
            return Entity.Dispatcher.Invoke(new UObjectEntity.GetPropertyHandler(Entity.GetProperty), new object[] { type });
        }

        private void SetProperty(UObjectEntity.PropertyType type, object value)
        {
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                if (Entity != null)
                    Entity.Dispatcher.BeginInvoke(new UObjectEntity.SetPropertyHandler(Entity.SetProperty), new object[] { type, value, false });
            }
        }

        private object CallMethod(UObjectEntity.MethodType type, object[] param)
        {
            if (!DesignerProperties.GetIsInDesignMode(this) && Entity != null)
            {
                return Entity.Dispatcher.Invoke(
                    new UObjectEntity.CallMethodHandler(Entity.CallMethod), new object[] { type, param });
            }
            else
                return null;
        }

        internal void RaiseObjectEvent(ObjectEventType type, object[] param)
        {
            switch (type)
            {
                case ObjectEventType.Closed:
                    if (Closed != null)
                        Closed(this, new EventArgs());
                    break;
                case ObjectEventType.LayoutChanged:
                    if (LayoutChanged != null)
                        LayoutChanged(this, param[1] as ObjectLayoutChangedEventArgs);
                    break;
                case ObjectEventType.ObjectLoaded:
                    if (ObjectLoaded != null)
                        ObjectLoaded(this, new EventArgs());
                    break;
                case ObjectEventType.LayoutStateChanged:
                    if (LayoutStateChanged != null)
                        LayoutStateChanged(this, new EventArgs());
                    break;
                case ObjectEventType.OperatingStateChanged:
                    if (OperatingStateChanged != null)
                        OperatingStateChanged(this, new EventArgs());
                    break;
                case ObjectEventType.OwnerChanged:
                    if (OwnerChanged != null)
                        OwnerChanged(this, new EventArgs());
                    break;
                case ObjectEventType.ChildrenChanged:
                    if (ChildrenChanged != null)
                        ChildrenChanged(this, param[1] as ObjectChildrenChangedEventArgs);
                    break;
				case ObjectEventType.DragStarted:
					if (DragStarted != null)
						DragStarted(this, param[1] as ObjectDragEventArgs);
					break;
				case ObjectEventType.DragEnded:
					if (DragEnded != null)
						DragEnded(this, param[1] as ObjectDragEventArgs);
					break;
                case ObjectEventType.IsActiveChanged:
                    bool newValue = (bool)param[0];
                    this.IsActive = newValue;
                    break;
            }
        }

        internal object CallLocalMethod(UObjectEntity.MethodType type, object[] param)
        {
            switch (type)
            {
                case UObjectEntity.MethodType.BeginAnimation:
                    this.BeginAnimation(param[0] as ObjectAnimationArgs,
                        param[1] as ObjectAnimationCompletedHandler);
                    return null;
                case UObjectEntity.MethodType.Close:
                    this.Close();
                    return null;
                case UObjectEntity.MethodType.TranslatePoint:
                    return this.TranslatePoint((Point)param[0],
                        param[1] as IObject);
                case UObjectEntity.MethodType.CaptureFinger:
                    return this.CaptureFinger((int)param[0]);
                case UObjectEntity.MethodType.UncaptureFinger:
                    return this.UncaptureFinger((int)param[0]);
                case UObjectEntity.MethodType.OnInputReceived:
                    this.OnInputReceived((InputEventArgs)param[0]);
                    return null;
                case UObjectEntity.MethodType.RaiseEvent:
                    this.RaiseEvent((InputEventArgs)param[0]);
                    return null;
                case UObjectEntity.MethodType.SendMessage:
                    this.SendMessage(param[0]);
                    return null;
                default:
                    return null;
            }
        }

        internal delegate void RaiseObjectEventHandler(ObjectEventType type, object[] param);

        /*
        public double MinHeight
        {
            get { return base.MinHeight; }
            set
            {
                if (base.MinHeight != value)
                {
                    base.MinHeight = value;
                }
            }
        }

        public double MinWidth 
        {
            get { return base.MinWidth; }
            set 
            {
                if (base.MinWidth != value)
                {
                    base.MinWidth = value;
                }
            }
        }

        double MaxWidth 
        {
            get { return base.MaxWidth; }
            set
            {
                if (base.MaxWidth != value)
                {
                    base.MaxWidth = value;
                }
            }
        }

        double MaxHeight 
        {
            get { return base.MaxHeight; }
            set
            {
                if (base.MaxHeight != value)
                {
                    base.MaxHeight = value;
                }
            }
        }*/

        public Point Position
        {
            get
            {
                if (IsDesigning)
                    return this._defaultPosition;
                else
                    return (Point)GetProperty(UObjectEntity.PropertyType.Position);
            }
            set
            {
                if (IsDesigning)
                    _defaultPosition = value;
                else
                    SetProperty(UObjectEntity.PropertyType.Position, value);
            }
        }

        public double Orientation
        {
            get
            {
                if (IsDesigning)
                    return this._defaultOrientation;
                else
                    return (double)GetProperty(UObjectEntity.PropertyType.Orientation);
            }
            set
            {
                if (IsDesigning)
                    _defaultOrientation = value;
                else
                    SetProperty(UObjectEntity.PropertyType.Orientation, value);
            }
        }

        public Point RotateCenter
        {
            get
            {
                if (IsDesigning)
                    return _defaultRotateCenter;
                else
                    return (Point)GetProperty(UObjectEntity.PropertyType.RotateCenter);
            }
            set
            {
                if (IsDesigning)
                    _defaultRotateCenter = value;
                else
                    SetProperty(UObjectEntity.PropertyType.RotateCenter, value);
            }
        }

        public double ScaleX
        {
            get
            {
                if (IsDesigning)
                    return _defaultScale.Width;
                else
                    return (double)GetProperty(UObjectEntity.PropertyType.ScaleX);
            }
            set
            {
                if (IsDesigning)
                    _defaultScale = new Size(value, _defaultScale.Height);
                else
                    SetProperty(UObjectEntity.PropertyType.ScaleX, value);
            }
        }

        public double ScaleY
        {
            get
            {
                if (IsDesigning)
                    return _defaultScale.Height;
                else
                    return (double)GetProperty(UObjectEntity.PropertyType.ScaleY);
            }
            set
            {
                if (IsDesigning)
                    _defaultScale = new Size(_defaultScale.Width, value);
                else
                    SetProperty(UObjectEntity.PropertyType.ScaleY, value);
            }
        }

        public Point ScaleCenter
        {
            get
            {
                if (IsDesigning)
                    return _defaultScaleCenter;
                else
                    return (Point)GetProperty(UObjectEntity.PropertyType.ScaleCenter);
            }
            set
            {
                if (IsDesigning)
                    _defaultScaleCenter = value;
                else
                    SetProperty(UObjectEntity.PropertyType.ScaleCenter, value);
            }
        }

        public int ZIndex
        {
            get
            {
                if (IsDesigning)
                    return _defaultZIndex;
                else
                    return (int)GetProperty(UObjectEntity.PropertyType.ZIndex);
            }
            set
            {
                if (IsDesigning)
                    _defaultZIndex = value;
                else
                    SetProperty(UObjectEntity.PropertyType.ZIndex, value);
            }
        }

        public IObject Owner
        {
            get 
            { 
                UObjectProxy proxy = (UObjectProxy)GetProperty(UObjectEntity.PropertyType.Owner);
                return proxy;
            }
            set { SetProperty(UObjectEntity.PropertyType.Owner, value); }
        }

        public ReadOnlyCollection<IObject> OwnedObjects
        {
            get 
            {
                ReadOnlyCollection<IObject> objects = (ReadOnlyCollection<IObject>)GetProperty(UObjectEntity.PropertyType.OwnedObjects);
                return objects;
            }
        }

        public ObjectOperatingState OperatingState
        {
            get
            {
                if (IsDesigning)
                    return _defaultOperatingState;
                else
                    return (ObjectOperatingState)GetProperty(UObjectEntity.PropertyType.OperatingState);
            }
            set
            {
                if (IsDesigning)
                    _defaultOperatingState = value;
                else
                {
                    SetProperty(UObjectEntity.PropertyType.OperatingState, value);
                }
                switch (OperatingState)
                {
                    case ObjectOperatingState.Automatic:
                    case ObjectOperatingState.Editing:
                        TurnToEdit();
                        break;
                    case ObjectOperatingState.Viewing:
                        TurnToView();
                        break;
                }
            }
        }

        public ObjectLayoutState LayoutState
        {
            get
            {
                if (IsDesigning)
                    return _defaultLayoutState;
                else
                    return (ObjectLayoutState)GetProperty(UObjectEntity.PropertyType.LayoutState);
            }
            set
            {
                if (IsDesigning)
                    _defaultLayoutState = value;
                else
                    SetProperty(UObjectEntity.PropertyType.LayoutState, value);
                switch (LayoutState)
                {
                    case ObjectLayoutState.Maximized:
                        _savedMenuOrientation = MenuOrientation;
                        _savedMenuPosition = MenuPosition;
                        _savedShowActionEffects = ShowsActivationEffects;
                        _savedMenuAngle = _menu.MaxAngle;
                        _savedMenuRadius = _menu.Radius;

                        ShowsActivationEffects = false;
                        MenuOrientation = -45;
                        MenuPosition = new Point(0, 0);
                        _menu.MaxAngle = 75;
                        break;
                    case ObjectLayoutState.Normal:
                        ShowsActivationEffects = _savedShowActionEffects;
                        MenuOrientation = _savedMenuOrientation;
                        MenuPosition = _savedMenuPosition;
                        _menu.MaxAngle = _savedMenuAngle;
                        _menu.Radius = _savedMenuRadius;
                        break;
                }
            }
        }

        public bool CanHoldObjects
        {
            get
            {
                if (IsDesigning)
                    return _defaultCanHoldObjects;
                else
                    return (bool)GetProperty(UObjectEntity.PropertyType.CanHoldObjects);
            }
            set
            {
                if (IsDesigning)
                    _defaultCanHoldObjects = value;
                else
                    SetProperty(UObjectEntity.PropertyType.CanHoldObjects, value);
            }
        }

        public bool NotifyLayoutChangeEnabled
        {
            get
            {
                if (IsDesigning)
                    return _defaultNotifyLayoutChangeEnabled;
                else
                    return (bool)GetProperty(UObjectEntity.PropertyType.NotifyLayoutChangeEnabled);
            }
            set
            {
                if (IsDesigning)
                    _defaultNotifyLayoutChangeEnabled = value;
                else
                    SetProperty(UObjectEntity.PropertyType.NotifyLayoutChangeEnabled, value);
            }
        }

        public Type ObjectType
        {
            get { return this.GetType(); }
        }

		/// <summary>
		/// Invoked when other objects send message to this object
		/// </summary>
		/// <param name="message">the message received</param>
        public virtual void OnMessageReceived(object message)
        {

        }

		/// <summary>
		/// Invoked when other objects are querying data from this object
		/// </summary>
		/// <param name="dataFlag">the flag that marks the data needed</param>
		/// <returns>the data that this object provides</returns>
		public virtual object OnDataQuerying(object dataFlag)
		{
			return null;
		}

        public void SendMessage(object message)
        {
			bool messageHandled = false;
			if (message is InitializeModelMessage)
			{
				this.InitializeModel((message as InitializeModelMessage).Model);
				messageHandled = true;
			}
			else if (message is UpdateModelMessage)
			{
				this.UpdateModel((message as UpdateModelMessage).UpdateMessage);
				messageHandled = true;
			}

			if (!messageHandled)
				OnMessageReceived(message);
        }

		public void QueryData(object dataFlag, QueryDataCallback callback)
		{
			object data = OnDataQuerying(dataFlag);
			if (callback != null)
				callback.BeginInvoke(new UObjectProxy(this.Entity), dataFlag, data, null, null);
		}

		public object QueryData(object dataFlag)
		{
			if (dataFlag is QueryModelMessage)
			{
				return GetModel();
			}

			return OnDataQuerying(dataFlag);
		}

        public Point TranslatePoint(Point point, IObject relativeTo)
        {
            return (Point)CallMethod(UObjectEntity.MethodType.TranslatePoint, new object[] { point, relativeTo });
        }

        public void Close()
        {
			if (UTableHelper.ShareCenter.IsSharing(this))
			{
				UTableHelper.ShareCenter.RemoveShare(this);
			}
			VisitObjectControls();

            CallMethod(UObjectEntity.MethodType.Close, new object[] { });
            
            // do some clean up work here
            this._entity = null;
            DependencyPropertyDescriptor.FromProperty(UObject.MinWidthProperty,
                    typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnMinWidthChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.MaxWidthProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnMaxWidthChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.MaxHeightProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnMaxHeightChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.MinHeightProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnMinHeightChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.WidthProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnWidthChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.HeightProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnHeightChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.IsHitTestVisibleProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnIsHitTestVisibleChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.OpacityProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnOpacityChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.VisibilityProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnVisibilityChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.ClipProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnClipChanged));
            DependencyPropertyDescriptor.FromProperty(UObject.ClipProperty,
                typeof(UObject)).RemoveValueChanged(this, new EventHandler(OnBackgroundChanged));
            this.Info.Control = null;

            DisposeControls(this);

            this.Dispatcher.InvokeShutdown();
        }

		void VisitObjectControls()
		{
			VisitObjectControls(this);
		}

		void VisitObjectControls(DependencyObject parent)
		{
			int count = VisualTreeHelper.GetChildrenCount(parent);
			for (int i = 0; i < count; i++)
			{
				DependencyObject child = VisualTreeHelper.GetChild(parent, i);
				if (child is UObject)
				{
					// just close the object
					(child as UObject).Close();
				}
				else
				{
					VisitObjectControls(child);
				}
			}
		}

        void DisposeControls(DependencyObject parent)
        {
            int count = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                if (child is IControlDisposable)
                {
                    // just close the object
                    (child as IControlDisposable).DisposeControl();
                }
                else
                {
                    DisposeControls(child);
                }
            }
        }

        private void BeginAnimation(ObjectAnimationArgs args, ObjectAnimationCompletedHandler completedCallback)
        {
            if (args == null)
                return;
            args.Animation.Freeze();
            CallMethod(UObjectEntity.MethodType.BeginAnimation, new object[] { args, completedCallback });
        }

        public event ObjectLayoutChangedEventHandler LayoutChanged;

        public event EventHandler ObjectLoaded;

        public event EventHandler Closed;

        public event EventHandler OwnerChanged;

        public event ObjectChildrenChangedEventHandler ChildrenChanged;

        public event EventHandler LayoutStateChanged;

        public event EventHandler OperatingStateChanged;

		public event ObjectDragEventHandler DragStarted;

		public event ObjectDragEventHandler DragEnded;

        #endregion
    }
	
	public enum UObjectActivationEffectType
	{
		SheenAndBorder,
		SheenAndMove,
		Sheen,
		Move,
	}

    public enum UObjectMenuDisplayType
    {
        AlwaysShow,
        HoldToShow,
        Hide,
    }
}
